CloudFront + Lambda 関数 URL 構成でPOST/PUT リクエストを行うため Lambda@Edge でコンテンツハッシュを計算する

CloudFront + Lambda 関数 URL 構成でPOST/PUT リクエストを行うため Lambda@Edge でコンテンツハッシュを計算する

Lambda 関数 URL で POST/PUTしたい場合はペイロードのハッシュをLambda@Edgeで計算しましょう
Clock Icon2024.07.29

こんにちは。リテールアプリ共創部のきんじょーです。

CloudFrontとLambda関数URLの構成で、POST/PUTリクエストを行う必要があり、少し前に以下の記事を書きました。

https://dev.classmethod.jp/articles/cloudfront-lambda-url-sigv4-signer/

実際のところ、Lambda 関数 URLでPUT、POSTリクエストを処理する際にはx-amz-content-sha256ヘッダーでSHA256で計算したリクエストボディのハッシュ値を含めるのみで良く、OACの設定と併用することでSigv4の署名は不要でした。

より良い方法をこちらのIssueで教えていただいたので、Sigv4の署名を付与する場合とのコードの差分を中心にこのブログでご紹介します。

Lambda関数URLでPOST/PUTをするサンプルコードの全量は以下のリポジトリに格納しています。

https://github.com/joe-king-sh/lambda-function-urls-with-post-put-sample

やってみる

ペイロードのハッシュを計算

Sigv4の署名を生成する処理に比べて、Lambda@Edgeのコードがかなりシンプルになりました。

calculate-content-hash.ts
import { CloudFrontRequestEvent, CloudFrontRequestHandler } from "aws-lambda";

const hashPayload = async (payload) => {
  const encoder = new TextEncoder().encode(payload);
  const hash = await crypto.subtle.digest("SHA-256", encoder);
  const hashArray = Array.from(new Uint8Array(hash));
  return hashArray.map((bytes) => bytes.toString(16).padStart(2, "0")).join("");
};

export const handler: CloudFrontRequestHandler = async (
  event: CloudFrontRequestEvent,
  _context,
) => {
  const request = event.Records[0].cf.request;
  console.log("originalRequest", JSON.stringify(request));

  if (!request.body?.body) {
    return request;
  }

  const body = request.body.data;
  const decodedBody = Buffer.from(body, "base64").toString("utf-8");

  request.headers["x-amz-content-sha256"] = [
    { key: "x-amz-content-sha256", value: await hashPayload(decodedBody) },
  ];
  console.log("modifiedRequest", JSON.stringify(request));

  return request;
};

Lambda@Edge の CDK 実装

Lambda@Edge側ではSigv4の署名を付与しなくなったのでOACのリソースをCDKで定義する必要があります。

server-stack.ts
+    const cfnOriginAccessControl =
+      new cdk.aws_cloudfront.CfnOriginAccessControl(
+        this,
+        "OriginAccessControl",
+        {
+          originAccessControlConfig: {
+            name: "Origin Access Control for Lambda Function URLs",
+            originAccessControlOriginType: "lambda",
+            signingBehavior: "always",
+            signingProtocol: "sigv4",
+          },
+        },
+      );
+
+    const cfnDistribution = cloudFrontDistribution.node
+      .defaultChild as cdk.aws_cloudfront.CfnDistribution;
+
+    // Set OAC
+    cfnDistribution.addPropertyOverride(
+      "DistributionConfig.Origins.0.OriginAccessControlId",
+      cfnOriginAccessControl.attrId,
+    );

まとめ

Lambda関数URLのIAM認証のためのSigv4署名の付与を、Lambda@EdgeではなくOACで設定するようになり、Lambda@Edgeのコードがシンプルになりました。

今後はこちらの構成でLambda関数URLをセキュアに公開したいと思います。

以上。リテールアプリ共創部のきんじょーでした。

参考

Issueを起票していただきありがとうございました!

https://github.com/joe-king-sh/lambda-function-urls-with-post-put-sample/issues/1

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.